
#ifndef __COMM_SERIAL_CPP__
#define __COMM_SERIAL_CPP__

#include "comm/comm_serial.h"
#include "comm/comm_base.h"

#include <math.h>

#include <string.h>
#include <list>
#include <vector>


#include <stdio.h>
#include <stdlib.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <iostream>
#ifndef WIN32
#include <termios.h>
#endif

using namespace std;

/**************************************************************************************
功能描述: 串口 判断是否可用
修改记录:
**************************************************************************************/
BOOL SERIAL_C::IsValid(void)
{
    return (fileDes_ >= 0);
}

/**************************************************************************************
功能描述: 串口 打开
修改记录:
**************************************************************************************/
INT32 SERIAL_C::Open(const char *pcDevName)
{
    fileDes_ = open(pcDevName, O_RDWR|O_NOCTTY|O_NDELAY);
    if (fileDes_ < 0) {
        ST_LOG_ERR("Open Seial Port(%s) Fail.", pcDevName);
        return -1;
    }

    int rslt = fcntl(fileDes_, F_SETFL, 0);
    if (rslt < 0) {
        ST_LOG_ERR("fcntl(fileDes_=%d) Fail.", rslt);
        return -1;
    }

    if (isatty(STDIN_FILENO) == 0) {
        ST_LOG_ERR("Is Not A Terminal Dev.");
        return -1;
    }

    /* 8 databits, 1 stopbit, no parity */
    if (Set(fileDes_, 115200, 8, 1, 'n') != 0) {
        ST_LOG_ERR("Set Port Attr Fail.");
        return -1;
    }

    return 0;
}

/**************************************************************************************
功能描述: 串口 打开
修改记录:
**************************************************************************************/
INT32 SERIAL_C::Open(const char *pcDevName, int speed)
{
    fileDes_ = open(pcDevName, O_RDWR|O_NOCTTY|O_NDELAY);
    if (fileDes_ < 0) {
        ST_LOG_ERR("Open Seial Port(%s) Fail.", pcDevName);
        return -1;
    }

    int rslt = fcntl(fileDes_, F_SETFL, 0);
    if (rslt < 0) {
        ST_LOG_ERR("fcntl(fileDes_=%d) Fail.", rslt);
        return -1;
    }

    if (isatty(STDIN_FILENO) == 0) {
        ST_LOG_ERR("Is Not A Terminal Dev.");
        return -1;
    }

    /* 8 databits, 1 stopbit, no parity */
    if (Set(fileDes_, speed, 8, 1, 'n') != 0) {
        ST_LOG_ERR("Set Port Attr Fail.");
        return -1;
    }

    return 0;
}

/**************************************************************************************
功能描述: 串口 关闭
修改记录:
**************************************************************************************/
void SERIAL_C::Close(void)
{
    if (fileDes_ >= 0) {
        close(fileDes_);
        fileDes_ = -1;
    }
}

/*******************************************************************************
 * Function: treadn
 * Identifier:  (Trace to: )
 * Description: continuiosly received nbytes data to buf until receive nbytes or timout
 *
 * Input:  fd: serial device   buf: recv buffer   nbytes:num of bytes desired to recv     timout:recv timeout value
 * Output: buf:recv buffer
 * Return: number of bytes we received
 * Others:
 * Log: 2015/10/21 Laizl create.
 *******************************************************************************/
INT32 SERIAL_C::ReadData(UINT08 *buf, UINT32 nbytes)
{
    if (fileDes_ < 0) {
        return -1;
    }

    int nleft;
    int nread;

    nleft = int(nbytes);
    while (nleft > 0)
    {
        /*recv until timeout or recv SERIAL_MSG_MAX_LEN data*/
        //10000 usec (10ms) character interval. we define the frame interval is 100ms and the character interval in a frame   should be less than 5ms at 115200bps
        if ((nread = tread(fileDes_, buf, UINT32(nleft), SERIAL_READ_TIMEOUT_LEN)) < 0)
        {
             if (nleft == int(nbytes))
             {
                return(-1); /* error, return -1 */
             }
             else
             {
                break;      /* error, return amount read so far */
             }
        } else if (nread == 0)
        {
             break;          /* EOF */
        }

        //Comm_PrintStream(buf, nread);

        nleft -= nread;
        buf += nread;
    }

    return (int(nbytes) - nleft);      /* return >= 0 */
}

/*******************************************************************************
 * Function: serialPutData
 * Identifier:  (Trace to: )
 * Description: send len of bytes to serial port
 *
 * Input:  pbuf:data to send     len:bytes num to send
 * Output: none
 * Return: void
 * Others:
 * Log: 2015/10/21 Laizl create.
 *******************************************************************************/
void SERIAL_C::SendData(const UINT08 *pbuf, int len)
{
    const UINT08 *ptmp = pbuf;
    if ((fileDes_ < 0) || (NULL == pbuf) || (0 == len))
    {
        return;
    }

    for (int i = 0; i < len; i++)
    {
        if (1 != write(fileDes_, ptmp, 1)) {
            ST_LOG_ERR("No Send 1 Byte.");
        }
        ptmp++;
    }
}

/**************************************************************************************
功能描述: 串口 设置参数
          param  fd     serial device file description
          param  nBits value should be 7 or 8
          param  nStop value should be 1 or 2
          param  nEvent  value should be N,E,O,,S
修改记录:
**************************************************************************************/
int SERIAL_C::Set(int fd, int nSpeed, int nBits, char nEvent, int nStop)
{
    struct termios newtio,oldtio;

    if (tcgetattr(fd, &oldtio) != 0) {
        ST_LOG_ERR("Get Attr Fail.");
        return -1;
    }

    bzero(&newtio, sizeof(newtio));
    newtio.c_cflag |=  CLOCAL | CREAD;
    newtio.c_cflag &= ~CSIZE;

    switch (nBits)
    {
        case 7:
            newtio.c_cflag |= CS7;
            break;

        case 8:
            newtio.c_cflag |= CS8;
            break;

        default:
            break;
    }

    switch (nEvent)
    {
        case 'O':
            newtio.c_cflag |= PARENB;
            newtio.c_cflag |= PARODD;
            newtio.c_iflag |= (INPCK | ISTRIP);
            break;

        case 'E':
            newtio.c_iflag |= (INPCK | ISTRIP);
            newtio.c_cflag |= PARENB;
            newtio.c_cflag &= ~PARODD;
            break;

        case 'N':
            newtio.c_cflag &= ~PARENB;
            break;

        default:
            break;
    }

    switch (nSpeed)
    {
        case 2400:
            cfsetispeed(&newtio, B2400);
            cfsetospeed(&newtio, B2400);
            break;

        case 4800:
            cfsetispeed(&newtio, B4800);
            cfsetospeed(&newtio, B4800);
            break;

        case 9600:
            cfsetispeed(&newtio, B9600);
            cfsetospeed(&newtio, B9600);
            break;

        case 19200:
            cfsetispeed(&newtio, B19200);
            cfsetospeed(&newtio, B19200);
            break;

        case 115200:
            cfsetispeed(&newtio, B115200);
            cfsetospeed(&newtio, B115200);
            break;

        case 460800:
            cfsetispeed(&newtio, B460800);
            cfsetospeed(&newtio, B460800);
            break;

        default:
            cfsetispeed(&newtio, B9600);
            cfsetospeed(&newtio, B9600);
            break;
    }

    if (nStop == 1) {
        newtio.c_cflag &= ~CSTOPB;
    } else if ( nStop == 2 ) {
        newtio.c_cflag |= CSTOPB;
    } else {
    }

    newtio.c_cc[VTIME] = 0;
    newtio.c_cc[VMIN]  = 0;

    tcflush(fd, TCIFLUSH);
    if(tcsetattr(fd,TCSANOW,&newtio) !=0 )
    {
        ST_LOG_ERR("Set Attr Fail.");
        return -1;
    }

    return 0;
}

/*******************************************************************************
 * Function: tread
 * Identifier:  (Trace to: )
 * Description: received nbytes data to buf, return if receive succeed or timout
 *
 * Input:  fd: serial device   buf: recv buffer   nbytes:num of bytes desired to recv     timout:recv timeout value
 * Output: buf:recv buffer
 * Return: number of bytes we received
 * Others: only receive one time
 * Log: 2015/10/21 lzl create.
 *******************************************************************************/
int SERIAL_C::tread(int fd, UINT08 *buf, size_t nbytes, unsigned int timout)
{
    int   nfds;
    fd_set  readfds;
    struct timeval  tv;

    tv.tv_sec = 0;
    tv.tv_usec = timout;

    FD_ZERO(&readfds);
    FD_SET(fd, &readfds);

    nfds = select(fd+1, &readfds, NULL, NULL, &tv);
    if (nfds < 0) {
        return -1;
    } else if (nfds == 0) {
        return 0;
    } else {
        return int(read(fd, buf, nbytes));
    }
}

/**************************************************************************************
功能描述: 申请发送消息缓存
修改记录:
**************************************************************************************/
SERIAL_SEND_MSG_STRU *SERIAL_SEND_MSG_BUFF_C::AllocMsg(void)
{
    R_ASSERT(idleBuffIdx_ < ARRAY_SIZE(astMsg_), NULL);

    /* 如果idleMsgBuffIdx_指向的缓存时空的，则使用该缓存，idx指向下一个可能是空的缓存，
       如果该缓存被使用了，则说明缓存列表已满。*/
    SERIAL_SEND_MSG_STRU *pstCur = &astMsg_[idleBuffIdx_];
    if (pstCur->msgLen == 0) {
        idleBuffIdx_ = (idleBuffIdx_ >= (ARRAY_SIZE(astMsg_) - 1)) ? 0 : (idleBuffIdx_ + 1);
        return pstCur;
    } else {
        return NULL;
    }
}

/**************************************************************************************
功能描述: 获取处于发送状态的消息
修改记录:
**************************************************************************************/
SERIAL_SEND_MSG_STRU *SERIAL_SEND_MSG_BUFF_C::GetSendingMsg(void)
{
    R_ASSERT(sendingMsgIdx_ < ARRAY_SIZE(astMsg_), NULL);

    /* 如果sendMsgBuffIdx_指向的缓存存在消息，则该消息就是当前正在发送的消息，
       如果该缓存中没有消息，则说明缓存列表时空的，都没有消息 */
    SERIAL_SEND_MSG_STRU *pstCur = &astMsg_[sendingMsgIdx_];
    if (pstCur->msgLen == 0) {
        return NULL;
    } else {
        return pstCur;
    }
}

/**************************************************************************************
功能描述: 释放当前处于发送状态的消息
修改记录:
**************************************************************************************/
void SERIAL_SEND_MSG_BUFF_C::FreeSendingMsg(void)
{
    R_ASSERT(sendingMsgIdx_ < ARRAY_SIZE(astMsg_), NOTHING);

    /* 如果sendMsgBuffIdx_指向缓存时空的，则说明当前没有发送的消息，
       如果该缓冲中有消息，则清除该消息，idx指向下一个可能存在消息的缓存 */
    SERIAL_SEND_MSG_STRU *pstCur = &astMsg_[sendingMsgIdx_];
    if (pstCur->msgLen != 0) {
        pstCur->msgLen = 0;

        sendingMsgIdx_       = (sendingMsgIdx_ >= (ARRAY_SIZE(astMsg_) - 1)) ? 0 : (sendingMsgIdx_ + 1);
        sendingMsgSendTimes_ = 0;
    }
}

/**************************************************************************************
功能描述: 当前发送消息增加1次发送次数
修改记录:
**************************************************************************************/
void SERIAL_SEND_MSG_BUFF_C::AddSendingMsgSendTimes(void)
{
    ++sendingMsgSendTimes_;
}

/**************************************************************************************
功能描述: 判断当前发送消息是否达到最大发送次数
修改记录:
**************************************************************************************/
BOOL SERIAL_SEND_MSG_BUFF_C::IsSendingMsgSendMaxTimes(void)
{
    return (sendingMsgSendTimes_ >= SERIAL_MSG_REPEAT_SEND_MAX_TIMES);
}

/**************************************************************************************
功能描述: 串口管理 静态全局变量
修改记录:
**************************************************************************************/
SERIAL_C                    SERIAL_MNG_C::objSerial_;
MSG_PROC_BASE_C            *SERIAL_MNG_C::pobjReceiveMsg_                              = NULL;
MSG_PASS_BASE_C            *SERIAL_MNG_C::pobjPassMsg_                                 = NULL;
bool                        SERIAL_MNG_C::isComPortError_                              = false;
SERIAL_ADDR_ENUM            SERIAL_MNG_C::enLocAddr_                                   = SERIAL_ADDR_BUTT;
SERIAL_ADDR_ENUM            SERIAL_MNG_C::enPeerAddr_                                  = SERIAL_ADDR_BUTT;
pthread_mutex_t             SERIAL_MNG_C::sendMutex_;
SERIAL_ADDR_FRAME_INFO_STRU SERIAL_MNG_C::astAddrFrameInfo_[SERIAL_ADDR_BUTT];
SERIAL_SEND_MSG_BUFF_C      SERIAL_MNG_C::objSendMsgBuffMng_;
double                      SERIAL_MNG_C::curMsgFrameSendTimeSec_                      = 0.0;
double                      SERIAL_MNG_C::lastTryToSendMsgTimeSec_                     = 0.0;
double                      SERIAL_MNG_C::lastSendMsgSuccTimeSec_                      = 0.0;
SERIAL_SEND_DBG_INFO_STRU   SERIAL_MNG_C::stSendDbgInfo_;
SERIAL_RECV_DBG_INFO_STRU   SERIAL_MNG_C::stRecvDbgInfo_;
UINT16                      SERIAL_MNG_C::ccittTable_[256] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
                                                               0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
                                                               0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
                                                               0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
                                                               0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
                                                               0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
                                                               0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
                                                               0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
                                                               0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
                                                               0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
                                                               0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
                                                               0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
                                                               0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
                                                               0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
                                                               0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
                                                               0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
                                                               0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
                                                               0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
                                                               0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
                                                               0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
                                                               0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
                                                               0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
                                                               0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
                                                               0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
                                                               0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
                                                               0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
                                                               0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
                                                               0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
                                                               0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
                                                               0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
                                                               0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
                                                               0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0};



/**************************************************************************************
功能描述: 串口管理 初始化
修改记录:
**************************************************************************************/
INT32 SERIAL_MNG_C::Init(SERIAL_ADDR_ENUM enLocAddr, SERIAL_ADDR_ENUM enPeerAddr, const char *pcDevName, MSG_PROC_BASE_C *pobjMsgProc, MSG_PASS_BASE_C *pobjMsgPass)
{
    R_ASSERT((pcDevName != NULL) && (pobjMsgProc != NULL), -1);

    enLocAddr_      = enLocAddr;
    enPeerAddr_     = enPeerAddr;
    pobjReceiveMsg_ = pobjMsgProc;
    pobjPassMsg_    = pobjMsgPass;

    double curTimeSec = Comm_GetTimeSec();
    lastTryToSendMsgTimeSec_ = curTimeSec;
    lastSendMsgSuccTimeSec_  = curTimeSec;

    if (0 != pthread_mutex_init(&sendMutex_, NULL)) {
        ST_LOG_ERR("Create Mutex Fail.");
        return -1;
    }

    if (0 != objSerial_.Open(pcDevName)) {
        ST_LOG_ERR("Open Dev(%s) Fail.", pcDevName);
        objSerial_.Close();
        return -1;
    }
    ST_LOG_INFO("Open Dev(%s) Succ.", pcDevName);

    if (0 != CreateThreadTsk()) {
        ST_LOG_ERR("Create Thread Fail.");
        objSerial_.Close();
        return -1;
    }
    ST_LOG_INFO("Create Thread Succ.");

    return 0;
}

/**************************************************************************************
功能描述: 串口管理 判断连接是否正常
修改记录:
**************************************************************************************/
bool SERIAL_MNG_C::IsConnectFault(void)
{
    return isComPortError_;
}

/**************************************************************************************
功能描述: 串口管理 发送消息
修改记录:
**************************************************************************************/
INT32 SERIAL_MNG_C::Send(SERIAL_ADDR_ENUM enTgtAddr, UINT08 msgType, const UINT08 *pMsg, UINT32 msgLen)
{
    return SendMsg(enTgtAddr, SERIAL_MSG_FRAME_TYPE_REQ, msgType, pMsg, msgLen);
}

/**************************************************************************************
功能描述: 串口管理 发送消息
修改记录:
**************************************************************************************/
INT32 SERIAL_MNG_C::Pass(const UINT08 *pMsg, UINT32 msgLen)
{
    pthread_mutex_lock(&sendMutex_);    /* 加解锁之间不要返回 */

    ++stSendDbgInfo_.passMsgCoun;

    //Comm_PrintStream(pMsg, msgLen);
    objSerial_.SendData(pMsg, msgLen);

    pthread_mutex_unlock(&sendMutex_);

    return 0;
}

/**************************************************************************************
功能描述: 串口管理 创建串口读取线程
修改记录:
**************************************************************************************/
INT32 SERIAL_MNG_C::CreateThreadTsk(void)
{
    if (0 != pthread_create(&tid_, NULL, SerialRecvTask, NULL)) {
        ST_LOG_ERR("Create Thread Fail.");
        return -1;
    }

    return 0;
}

/**************************************************************************************
功能描述: 串口管理 取消串口读取线程
修改记录:
**************************************************************************************/
void SERIAL_MNG_C::DeleteThreadTsk(void)
{
    if (0 != pthread_cancel(tid_)) {
        ST_LOG_ERR("Cancel Thread Fail.");
        return;
    }

    void *res = NULL;
    if (0 != pthread_join(tid_, &res)) {
        ST_LOG_ERR("Join Thread Fail.");
        return;
    }

    ST_LOG_INFO("Succ.");
}

/**************************************************************************************
功能描述: 计算CRC16校验码
        * @param data          计算的数据
        * @param len           数据的长度
        * @param oldCRC16      上一个CRC16的值，用于循环计算大文件的CRC16。第一个数据的CRC16则传入0x0。
修改记录:
**************************************************************************************/
UINT16 SERIAL_MNG_C::GetCRC16(const UINT08 *pucData, UINT32 dataLen, unsigned long oldCRC16)
{
    UINT16 crc16 = oldCRC16;

    while (dataLen-- > 0) {
        crc16 = ccittTable_[(crc16 >> 8 ^ *pucData++) & 0xff] ^ (crc16 << 8);
    }

    return crc16;
}

/**************************************************************************************
功能描述: 分配帧编号
修改记录:
**************************************************************************************/
UINT08 SERIAL_MNG_C::AllocSendFrameNo(UINT08 tgtAddr)
{
    R_ASSERT(tgtAddr < ARRAY_SIZE(astAddrFrameInfo_), 0xFF);

    ++astAddrFrameInfo_[tgtAddr].lastSendFrameNo;
    return astAddrFrameInfo_[tgtAddr].lastSendFrameNo;
}

/**************************************************************************************
功能描述: 判断本次收到的帧编号是否和上一次一样，通过此判断是否同一条消息
修改记录:
**************************************************************************************/
BOOL SERIAL_MNG_C::IsRecvFrameNoRepeat(UINT08 srcAddr, UINT08 recvFrameNo)
{
    R_ASSERT(srcAddr < ARRAY_SIZE(astAddrFrameInfo_), true);

    if (recvFrameNo != astAddrFrameInfo_[srcAddr].lastRecvFrameNo) {
        astAddrFrameInfo_[srcAddr].lastRecvFrameNo = recvFrameNo;
        return false;
    } else {
        return true;
    }
}

/**************************************************************************************
功能描述: 设置串口状态
修改记录:
**************************************************************************************/
void SERIAL_MNG_C::UptComPortState(double curTimeSec)
{
    if ((curTimeSec - lastSendMsgSuccTimeSec_) > SERIAL_NO_SEND_SUCC_TIME_OF_JUDGE_ERROR) {
        ST_IF_LOG_ERR(!isComPortError_, "COM is error.");
        isComPortError_ = true;
    } else {
        ST_IF_LOG_INFO(isComPortError_, "COM is ok.");
        isComPortError_ = false;
    }
}

/**************************************************************************************
功能描述: 输出统计信息
修改记录:
**************************************************************************************/
void SERIAL_MNG_C::OutputDbgInfo(double curTimeSec)
{
    static double lastOutputTimeSec = 0.0;

    if (lastOutputTimeSec == 0.0) {
        lastOutputTimeSec = curTimeSec;
        return;
    }

    if ((curTimeSec - lastOutputTimeSec) < (1.0 * 60.0)) {
        return;
    }

    lastOutputTimeSec = curTimeSec;

    stRecvDbgInfo_.Output();
    stSendDbgInfo_.Output();
}

/**************************************************************************************
功能描述: 缓存消息
修改记录:
**************************************************************************************/
void SERIAL_MNG_C::FillSendMsg(SERIAL_SEND_MSG_STRU *pstMsgBuff, SERIAL_ADDR_ENUM enTgtAddr, SERIAL_MSG_FRAME_TYPE_ENUM enFrameType, UINT08 msgType, const UINT08 *pMsg, UINT32 msgLen)
{
    SERIAL_MSG_HEAD_STRU *pstHead = (SERIAL_MSG_HEAD_STRU *)pstMsgBuff->aucMsg;
    pstHead->beginMark = SERIAL_MSG_BEGIN_MARK;
    pstHead->dataLen   = (UINT16)msgLen;
    pstHead->srcAddr   = enLocAddr_;
    pstHead->tgtAddr   = enTgtAddr;
    pstHead->frameType = enFrameType;
    pstHead->msgType   = msgType;
    pstHead->frameNo   = AllocSendFrameNo(enTgtAddr);

    if (msgLen != 0) {
        memcpy(pstHead + 1, pMsg, msgLen);
    }

    UINT32 msgHeadAndDataLen = sizeof(*pstHead) + msgLen;
    SERIAL_MSG_TAIL_STRU *pstTail = (SERIAL_MSG_TAIL_STRU *)&pstMsgBuff->aucMsg[msgHeadAndDataLen];
    pstTail->crc16 = GetCRC16((UINT08 *)pstHead, msgHeadAndDataLen, 0);

    pstMsgBuff->msgLen = SERIAL_MSG_MIN_LEN + msgLen;
}

/**************************************************************************************
功能描述: 发送缓存消息
          注意: 必须在缓存消息有效的时候才能调用，由调用方保证。
修改记录:
**************************************************************************************/
void SERIAL_MNG_C::SendBuffMsg(double curTimeSec)
{
    SERIAL_SEND_MSG_STRU *pstSendingMsg = objSendMsgBuffMng_.GetSendingMsg();
    /* 有消息，但没有处于等待应答时，立即发送消息 */
    if ((pstSendingMsg != NULL) && (curMsgFrameSendTimeSec_ == 0.0)) {
        objSerial_.SendData(pstSendingMsg->aucMsg, pstSendingMsg->msgLen);

        curMsgFrameSendTimeSec_ = curTimeSec;

        objSendMsgBuffMng_.AddSendingMsgSendTimes();
    }
}

/**************************************************************************************
功能描述: 发送消息
修改记录:
**************************************************************************************/
INT32 SERIAL_MNG_C::SendMsg(SERIAL_ADDR_ENUM enTgtAddr, SERIAL_MSG_FRAME_TYPE_ENUM enFrameType, UINT08 msgType, const UINT08 *pMsg, UINT32 msgLen)
{
    R_ASSERT((msgLen == 0) || ((msgLen <= SERIAL_MSG_DATA_MAX_LEN) && (pMsg != NULL)), -1);

    pthread_mutex_lock(&sendMutex_);    /* 加解锁之间不要返回 */

    double curTimeSec = Comm_GetTimeSec();

    ++stSendDbgInfo_.msgCount;
    lastTryToSendMsgTimeSec_ = curTimeSec;

    SERIAL_SEND_MSG_STRU *pstMsgBuff = objSendMsgBuffMng_.AllocMsg();
    if (pstMsgBuff == NULL) {
        ++stSendDbgInfo_.msgFailCountForBusy;
        pthread_mutex_unlock(&sendMutex_);
        return -1;
    }

    FillSendMsg(pstMsgBuff, enTgtAddr, enFrameType, msgType, pMsg, msgLen);

    SendBuffMsg(curTimeSec);

    pthread_mutex_unlock(&sendMutex_);

    return 0;
}

/**************************************************************************************
功能描述: 发送握手消息
修改记录:
**************************************************************************************/
void SERIAL_MNG_C::SendHandshakeMsg(void)
{
    /* 握手消息，功能码和消息内容无用 */
    (void)SendMsg(enPeerAddr_, SERIAL_MSG_FRAME_TYPE_HANDSHAKE, 0xFF, NULL, 0);
}


/**************************************************************************************
功能描述: 握手
修改记录:
**************************************************************************************/
void SERIAL_MNG_C::Handshake(double curTimeSec)
{
    if ((curTimeSec - lastTryToSendMsgTimeSec_) > SERIAL_MSG_HANDSHAKE_INTERVAL_TIME) {
        SendHandshakeMsg();
    }
}

/**************************************************************************************
功能描述: 回复应答帧
修改记录:
**************************************************************************************/
void SERIAL_MNG_C::SendAckFrame(const SERIAL_MSG_HEAD_STRU &stReqFrameHead, BOOL isCheckSucc)
{
    pthread_mutex_lock(&sendMutex_);    /* 加解锁之间不要返回 */

    SERIAL_MSG_ACK_FRAME_STRU stAckFrame;

    stAckFrame.stHead.beginMark = SERIAL_MSG_BEGIN_MARK;
    stAckFrame.stHead.dataLen   = 0;
    stAckFrame.stHead.srcAddr   = enLocAddr_;
    stAckFrame.stHead.tgtAddr   = stReqFrameHead.srcAddr;
    stAckFrame.stHead.frameType = isCheckSucc ? SERIAL_MSG_FRAME_TYPE_ACK_SUCC : SERIAL_MSG_FRAME_TYPE_ACK_FAIL;
    stAckFrame.stHead.msgType   = stReqFrameHead.msgType;
    stAckFrame.stHead.frameNo   = stReqFrameHead.frameNo;
    stAckFrame.stTail.crc16     = GetCRC16((UINT08 *)&stAckFrame.stHead, sizeof(stAckFrame.stHead), 0);

    objSerial_.SendData((UINT08 *)&stAckFrame, sizeof(stAckFrame));

    pthread_mutex_unlock(&sendMutex_);
}

/**************************************************************************************
功能描述: 判断是否为应答帧
修改记录:
**************************************************************************************/
BOOL SERIAL_MNG_C::IsAckFrame(const SERIAL_MSG_HEAD_STRU &stFrameHead)
{
    return (   (stFrameHead.frameType == SERIAL_MSG_FRAME_TYPE_ACK_SUCC)
            || (stFrameHead.frameType == SERIAL_MSG_FRAME_TYPE_ACK_FAIL));
}

/**************************************************************************************
功能描述: 判断消息是否需要应用处理
修改记录:
**************************************************************************************/
BOOL SERIAL_MNG_C::IsFrameNeedAppProc(const SERIAL_MSG_HEAD_STRU &stFrameHead)
{
    switch (stFrameHead.frameType) {
        case SERIAL_MSG_FRAME_TYPE_ACK_SUCC:
        case SERIAL_MSG_FRAME_TYPE_ACK_FAIL:
        case SERIAL_MSG_FRAME_TYPE_HANDSHAKE:
            return false;

        default:
            return true;
    }
}

/**************************************************************************************
功能描述: 判断是否需要应答帧
修改记录:
**************************************************************************************/
BOOL SERIAL_MNG_C::IsFrameNeedAck(const SERIAL_MSG_HEAD_STRU &stFrameHead)
{
    /* 最终消息处理模块，才需要应答，转发模块不需要，非应答帧才需要应答 */
    return ((stFrameHead.tgtAddr == enLocAddr_) && !IsAckFrame(stFrameHead));
}

/**************************************************************************************
功能描述: 处理应答帧
修改记录:
**************************************************************************************/
void SERIAL_MNG_C::ProcAckFrame(double curTimeSec, const SERIAL_MSG_HEAD_STRU &stAckFrameHead)
{
    pthread_mutex_lock(&sendMutex_);    /* 加解锁之间不要返回 */

    SERIAL_SEND_MSG_STRU *pstSendingMsg = objSendMsgBuffMng_.GetSendingMsg();
    if (pstSendingMsg != NULL) {
        SERIAL_MSG_HEAD_STRU *pstSendingMsgHead = (SERIAL_MSG_HEAD_STRU *)pstSendingMsg->aucMsg;

        /* 非本消息的应答消息不处理 */
        if ((stAckFrameHead.srcAddr == pstSendingMsgHead->tgtAddr) && (stAckFrameHead.frameNo == pstSendingMsgHead->frameNo)) {
            curMsgFrameSendTimeSec_ = 0.0;

            if (stAckFrameHead.frameType == SERIAL_MSG_FRAME_TYPE_ACK_SUCC) {
                objSendMsgBuffMng_.FreeSendingMsg();

                lastSendMsgSuccTimeSec_ = curTimeSec;

                stSendDbgInfo_.succMsgLenSum += pstSendingMsgHead->dataLen;
            } else {
                ++stSendDbgInfo_.frameFailCountForErrCheck;

                if (objSendMsgBuffMng_.IsSendingMsgSendMaxTimes()) {
                    objSendMsgBuffMng_.FreeSendingMsg();

                    ++stSendDbgInfo_.msgFailCountForTimesOver;
                }
            }
        }

        SendBuffMsg(curTimeSec);
    }

    pthread_mutex_unlock(&sendMutex_);
}

/**************************************************************************************
功能描述: 处理应答帧超时
修改记录:
**************************************************************************************/
void SERIAL_MNG_C::ProcAckFrameTimeout(double curTimeSec)
{
    pthread_mutex_lock(&sendMutex_);    /* 加解锁之间不要返回 */

    SERIAL_SEND_MSG_STRU *pstSendingMsg = objSendMsgBuffMng_.GetSendingMsg();
    if (pstSendingMsg != NULL) {
        /* 超时，如果重发次数超过最大次数，发送失败结束；否则继续发送 */
        if ((curTimeSec - curMsgFrameSendTimeSec_) > SERIAL_WAIT_ACK_FRAME_MAX_TIME_LEN) {
            curMsgFrameSendTimeSec_ = 0.0;

            ++stSendDbgInfo_.frameFailCountForRspTimeout;

            if (objSendMsgBuffMng_.IsSendingMsgSendMaxTimes()) {
                objSendMsgBuffMng_.FreeSendingMsg();

                ++stSendDbgInfo_.msgFailCountForTimesOver;
            }

            SendBuffMsg(curTimeSec);
        }
    }

    pthread_mutex_unlock(&sendMutex_);
}

/**************************************************************************************
功能描述: 消息数据处理
修改记录:
**************************************************************************************/
void SERIAL_MNG_C::ProcFrame(double curTimeSec, const SERIAL_MSG_HEAD_STRU &stHead, UINT08 *pucMsgCnt)
{
    if (IsAckFrame(stHead)) {
        ProcAckFrame(curTimeSec, stHead);

    } else if (IsFrameNeedAppProc(stHead)) {
        /* 过滤重复帧 */
        if (IsRecvFrameNoRepeat(stHead.srcAddr, stHead.frameNo)) {
            return;
        }

        stRecvDbgInfo_.succMsgLenSum += stHead.dataLen;

        /* 业务模块处理 */
        if (pobjReceiveMsg_ != NULL) {
            pobjReceiveMsg_->ProcMsg(stHead.srcAddr, stHead.msgType, pucMsgCnt, stHead.dataLen);
        }

    } else {
        /* 握手帧等不需要处理 */
    }
}

/**************************************************************************************
功能描述: 处理接收的数据
          返回已处理的数据的长度
修改记录:
**************************************************************************************/
UINT32 SERIAL_MNG_C::ProcRecvData(double curTimeSec, UINT08 *pucData, UINT32 dataLen)
{
    if (dataLen < SERIAL_MSG_MIN_LEN) {
        return 0;
    }

    UINT32 procDataLen = 0;
    for (UINT32 loop = 0; loop <= (dataLen - SERIAL_MSG_MIN_LEN); loop += procDataLen) {
        SERIAL_MSG_HEAD_STRU *pstMsgHead = (SERIAL_MSG_HEAD_STRU *)&pucData[loop];
        /* 没有匹配完整的一条消息时，继续匹配下一个字节 */
        if (pstMsgHead->beginMark != SERIAL_MSG_BEGIN_MARK) {
            ++stRecvDbgInfo_.discardDataLen;
            procDataLen = 1;
            continue;
        }

        /* 如果长度不够一条完整消息，则暂时不处理等待下次 */
        UINT32 curMsgLen = SERIAL_MSG_MIN_LEN + pstMsgHead->dataLen;
        if ((loop + curMsgLen) > dataLen) {
            return loop;
        }

        ++stRecvDbgInfo_.msgCount;

        /* 如果消息校验失败，可能是异常了，也可能是起始标志无匹配了，所以从下一个字节继续处理 */
        UINT32 msgHeadAndDataLen = sizeof(*pstMsgHead) + pstMsgHead->dataLen;
        SERIAL_MSG_TAIL_STRU *pstMsgTail = (SERIAL_MSG_TAIL_STRU *)&pucData[loop + msgHeadAndDataLen];
        BOOL isCheckSucc = (pstMsgTail->crc16 == GetCRC16((UINT08 *)pstMsgHead, msgHeadAndDataLen, 0));
        /* 回复应答帧 */
        if (IsFrameNeedAck(*pstMsgHead)) {
            SendAckFrame(*pstMsgHead, isCheckSucc);
        }
        if (!isCheckSucc) {
            ++stRecvDbgInfo_.msgCheckFailCount;
            ++stRecvDbgInfo_.discardDataLen;
            procDataLen = 1;
            continue;
        }

        /* 处理正常消息 */
        if (pstMsgHead->tgtAddr != enLocAddr_) {
            stRecvDbgInfo_.passMsgCount++;

            /* 源不能时本地地址，防止其它端伪造本地消息 */
            if ((pstMsgHead->srcAddr != enLocAddr_) && (pobjPassMsg_ != NULL)) {
                /* 消息转发，包括ack消息，握手消息，应用消息等 */
                pobjPassMsg_->Send(pstMsgHead->tgtAddr, (UINT08 *)pstMsgHead, curMsgLen);
            }
        } else {
            ProcFrame(curTimeSec, *pstMsgHead, (UINT08 *)(pstMsgHead + 1));
        }

        procDataLen = curMsgLen;
        continue;
    }

    return dataLen;
}

/**************************************************************************************
功能描述: 串口管理 收取消息线程任务处理函数
修改记录:
**************************************************************************************/
void *SERIAL_MNG_C::SerialRecvTask(void *arg)
{
    UINT32 leftDataLen = 0;
    UINT08 buff[SERIAL_MSG_MAX_LEN * 2];     /* 为了能合并连续两次接收的消息，所以该最大长度翻倍 */

    double curTimeSec = Comm_GetTimeSec();
    double lastReadDataSuccTimeSec = curTimeSec;

    while (true)
    {
        curTimeSec = Comm_GetTimeSec();

        /* 处理响应帧超时 */
        ProcAckFrameTimeout(curTimeSec);

        /* 握手 */
        Handshake(curTimeSec);

        /* 处理出口状态 */
        UptComPortState(curTimeSec);

        /* 输出维测信息 */
        OutputDbgInfo(curTimeSec);

        /***********************  从串口读取数据  *****************************/
        if (leftDataLen >= sizeof(buff)) {
            ++stRecvDbgInfo_.abnormalCount;
            leftDataLen = 0;
        }

        UINT32 freeBuffLen = sizeof(buff) - leftDataLen;
        INT32  readDataLen = objSerial_.ReadData(&buff[leftDataLen], freeBuffLen);
        if ((readDataLen < 0) || (readDataLen > INT32(freeBuffLen))) {
            ++stRecvDbgInfo_.abnormalCount;
            leftDataLen = 0;
            continue;

        } else if (readDataLen == 0) {
            /* 如果持续一段时间没有读取到数据，则说明之前遗留的数据无后续数据，应该丢弃 */
            if ((curTimeSec - lastReadDataSuccTimeSec) > SERIAL_MSG_SEND_BYTE_INTERVAL) {
                stRecvDbgInfo_.discardDataLen += leftDataLen;
                leftDataLen = 0;
            }

            continue;

        } else {
        }

        lastReadDataSuccTimeSec = curTimeSec;
        //Comm_PrintStream(&buff[leftDataLen], readDataLen);

        /***********************  处理读取的消息  *****************************/
        UINT32 dataLen     = leftDataLen + readDataLen;
        //Comm_PrintStream(buff, dataLen);
        UINT32 procDatalen = ProcRecvData(curTimeSec, buff, dataLen);
        if (procDatalen > dataLen) {
            ++stRecvDbgInfo_.abnormalCount;
            procDatalen = dataLen;
        }

        /* 将剩余数据移动到最前面 */
        leftDataLen = dataLen - procDatalen;
        if ((procDatalen != 0) && (leftDataLen > 0)) {
            memmove(buff, &buff[procDatalen], leftDataLen);
        }
    }

    return NULL;
}

#endif


